﻿#include "./pngdecoder.h"

using namespace MiniUI;

PngDecoder::PngDecoder(BaseIOStub* iostub)
: BaseDecoder(iostub)
{
}

PngDecoder::~PngDecoder()
{

}

bool PngDecoder::ReadImageInfo(DecodeFormat* deco)
{
    m_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)nullptr, nullptr, nullptr);
    if (!m_png_ptr)
    {
        return false;
    }
    m_info_ptr = png_create_info_struct(m_png_ptr);
    if (!m_info_ptr)
    {
        return false;
    }

    if (setjmp(png_jmpbuf(m_png_ptr)))
    {
        return false;
    }
    png_set_read_fn(m_png_ptr, m_iostub, png_read_mem);


    png_read_info(m_png_ptr, m_info_ptr);

    if (m_info_ptr->bit_depth == 16)
    {
        png_set_strip_16(m_png_ptr);
    }

    int pixelDepth = m_info_ptr->pixel_depth;

    if (m_info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
    {
    }
    else
    {
        if (!(m_info_ptr->color_type & PNG_COLOR_MASK_COLOR))
        {
            png_set_gray_to_rgb(m_png_ptr);
            png_set_expand(m_png_ptr);
        }
        else
        {
            if (m_info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
            {
                png_set_expand(m_png_ptr);
            }
            if (pixelDepth < 8)
            {
                png_set_expand(m_png_ptr);
            }
        }
    }

    if (png_get_valid(m_png_ptr, m_info_ptr, PNG_INFO_tRNS))
    {
        png_set_tRNS_to_alpha(m_png_ptr);
    }

    if (m_info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || (m_info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
    {
        if (m_info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
        {
            png_set_gray_to_rgb(m_png_ptr);
            png_set_expand(m_png_ptr);
        }
    }
    png_set_filler(m_png_ptr, 0xFF, PNG_FILLER_AFTER);

    if (m_info_ptr->color_type & PNG_COLOR_MASK_COLOR)
    {
        png_set_bgr(m_png_ptr);
    }


    png_read_update_info(m_png_ptr, m_info_ptr);

    if (m_info_ptr->pixel_depth != 32)
    {
        return false;
    }

    m_nPass = png_set_interlace_handling(m_png_ptr);

    deco->width = m_info_ptr->width;
    deco->height = m_info_ptr->height;

    return true;
}

bool PngDecoder::ReadImageFrame(DecodeFormat* deco)
{
    if (deco->width <= 0 || deco->width > 0x3FFF || deco->height <= 0 || deco->height > 0x3FFF 
        || deco->nOutWidthBytes < 0 || deco->nOutWidthBytes > 0x3FFF * 4 || !deco->pOutBuffer)
    {
        return false;
    }

    if (setjmp(png_jmpbuf(m_png_ptr)))
    {
        return false;
    }

    int height = m_png_ptr->height;


    m_png_ptr->num_rows = height; /* Make sure this is set correctly */

    for (int n = 0; n < m_nPass; n++)
    {
        byte * pbuffer = deco->pOutBuffer;

        for (int y = 0; y < height; y++)
        {
            png_read_row(m_png_ptr, pbuffer, nullptr);
            pbuffer += deco->nOutWidthBytes;
        }
    }

    png_read_end(m_png_ptr, m_info_ptr);

    return true;
}

bool PngDecoder::FinishDecode(DecodeFormat* deco)
{
    png_destroy_read_struct(&m_png_ptr, &m_info_ptr, nullptr);

    m_png_ptr = nullptr;
    m_info_ptr = nullptr;
    m_nPass = 0;

    return true;
}

bool PngDecoder::CheckFormat(const byte* pbuf, int nbuflen)
{
    if (!pbuf || nbuflen < 8)
    {
        return false;
    }

    return pbuf[0] == 0x89 &&
        pbuf[1] == 0x50 &&
        pbuf[2] == 0x4e &&
        pbuf[3] == 0x47 &&
        pbuf[4] == 0x0d &&
        pbuf[5] == 0x0a &&
        pbuf[6] == 0x1a &&
        pbuf[7] == 0x0a;
}

void PNGAPI PngDecoder::png_read_mem(png_structp png_ptr, png_bytep data, png_size_t length)
{
    if (png_ptr == NULL)
    {
        return;
    }

    BaseIOStub* stub = (BaseIOStub*)png_ptr->io_ptr;
    uint uRead = stub->Read(data, length);
    if (uRead < length)
    {
        png_error(png_ptr, "Read Error");
    }
}